The present invention concerns debugging programs and pertains particularly to the use of dynamic translation to provide breakpoints in non-writeable object code such as procedures within a shared library.
Programs are generally written in a high level programming language. This high level language, often referred to as source code, is translated by a compiler program into an assembly language. The binary form of the assembly language, called object code, is the form of the code actually executed by a computer. The object code is generally first produced in object code modules which are linked together by a linker For the purpose of the present application, the term "compile" includes both the process of producing the object code modules and linking the object code modules together.
Code debuggers are programs which aid a programmer in finding errors in code. They are extremely useful tools for improving the efficiency of the code debugging process. One of the important features of a code debugger is to allow a programmer to stop the execution of code and to check the values in each user resource the code is operating upon. A user resource is typically a variable defined in the source code. The values in the user resources give clues to indicate the source of trouble when a program is not operating correctly.
In order for a debugger to set a breakpoint at a particular address in an application, if the application is currently running, the application must be stopped. On computing systems which are operating under the UNIX operating system, this is typically done by sending the application a signal. Once the application is stopped, the debugger replaces the object code currently residing at the particular address with a special instruction which will cause execution to halt when it is reached. For example, for computing systems which are designed to execute a Precision Architecture Reduced Instruction Set Computer (PA-RISC) instruction set, a break instruction would typically be used to cause execution to halt. See "PA-RISC 1.1 Architecture and Instruction Set Reference Manual", Third Edition, Hewlett-Packard Company Part Number 09740-90039, p. 5-138.
After the break instruction is placed in the object code, the application is allowed to resume execution. When and if execution of the application reaches the particular address, the break instruction is encountered and execution of the application is halted. Control is then given to the debugger. To remove the breakpoint at the particular address, the debugger replaces the break instruction with the instruction that was originally at the particular address.
The above described scheme for introducing break instructions into application programs has been complicated by recent developments in the architecture of application programs. Specifically, application programs are now often built out of layers of shared and reusable code which perform common tasks. For example, display window management code, database code and statistical analysis code typically reside in procedures which are shared by many applications. The procedures are typically packaged in units called shared libraries (SL) or demand-loaded libraries (DLL). Procedures within a shared library are typically shared by many application programs.
The use of shared libraries introduces a problem for a debugging program. Specifically, use of shared libraries makes it difficult to set break points in an application program. If a debugger replaces an instruction in a shared library with a special break instruction, then any of the applications which are currently using the shared library could erroneously hit the break point and halt operation.
To solve this problem, debuggers typically cause the operating system to make a special copy of all shared libraries used by the application being debugged. Other applications are prevented from accessing the copy of the shared libraries. However, this can be a complex and expensive method for allowing debugging. This requires the operating system to make a copy of all of the shared code whether it will actually be executed or not. This can lead to quite extensive copying as it means that the operating system must copy all the shared code which could possibly be used by the application and any additional shared code which the copied shared code could utilize, and so on.
In addition, when shared code management employs methods in which the shared code is not made visible to the application until and if it is needed (i.e., load on demand), the task is significantly compounded in difficulty for the debugger. For example, when deferred binding is utilized, this delays the linkage of shared code until just before it is used at run-time. See, for example, Programming on HP-UX, HP 9000 Series 700/800 Computers, available as HP Part No. B2355-90652, from Hewlett-Packard Company, having a business address of 3000 Hanover Street, Palo Alto, Calif. 94304, January 1995, p 5-14.
This means that the need for shared code (and a special copy thereof) may not be evident before runtime. This makes it extremely difficult to determine which shared code could possibly be used by an application in time to make a copy of the shared code for use by the debugger.